package network;

import java.io.IOException;
import java.io.InputStream;
import java.security.PublicKey;

public class MessageReader {

	public MessageReader(InputStream in) {
		this(in,NetworkVars.bufSize);
	}
	public MessageReader(InputStream in, int sz) {
		this(in,sz,null);
	}
	public MessageReader(InputStream in, PublicKey k) {
		this(in,NetworkVars.bufSize,k);
	}
	public MessageReader(InputStream in, int sz, PublicKey k) {
		this.in = in;
		cb = new byte[sz];
		offset = 0;
		end = 0;
		key = k;
	}

	private final PublicKey key;

	private InputStream in;
	private byte[] cb;
	private int offset = 0;
	private int end = 0;

	private boolean isOpen = true;

	public String readMessage() throws IOException{
		if( offset >= end || in.available() > 0){
			if(key != null){
				byte[] enc = new byte[in.available()];
				in.read(enc);
				byte[] dec;
				try {
					byte[] dec1 = encryption.RSA.decrypt(enc, NetworkVars.prvKey);
					dec = encryption.RSA.decrypt(dec1, key);
				} catch (Exception e) {
					//Error with RSA decrypt, can't continue. throw IOException
					throw new IOException("Can't read stream, bad decryption.");
				}
				if(end-offset + dec.length > cb.length){
					repos();
					grow(dec.length+end-offset);
				}
				System.arraycopy(dec, 0, cb, end, dec.length);
				end = end + dec.length;//bump the end marker
			}else{
				int a = in.read(cb, offset, end);
				end = end + a;
			}
			
		}


		String ret = "";
		int zeroNum = 0;//number of (char)0 in a row
		boolean start = true;//begining of msg?


		int i = offset;//read position start.

		for( ; ;i++){
			if(i > end){
				//error this is not good.
				if(zeroNum >= 4 && !start){
					//it can be safe to assume this is the end.
					end = end + in.read(cb, i, cb.length-i);
					//break;
				}
			}
			if(cb[i] != Message.DELIMITER){//message delimiter. 

				ret += (char)cb[i];

			}else{ //if (zeroNum == 4 && !start)

				if(!start){//this might not actually be done yet.

					if(zeroNum >= 4){//should be done.
						i++;//skip past the delimiter.
						break;
					}

					ret += (char)cb[i];//this wasn't the end
				}

			}


			if((char)cb[i] == (char)Message.PADDING){
				zeroNum++;
				if(zeroNum >= 4){
					if(start){
						start = false;
						zeroNum = 0;
					}else{
						//next char should be delimiter.
					}
				}
			}else{
				zeroNum=0;
			}
		}//end for loop


		offset = i;

		repos();

		//blank the rest of the array, so we don't get messages after everything
		for(int q=end; q<cb.length; q++){
			cb[q]=0;
		}

		return ret;
	}


	public boolean ready(){
		try {
			return isOpen && ( in.available() > 9 || offset < end);
			//although putting the 'offset < end' first would be better, this way we can ensure
			//that we will notice if there is an abrupt interruption of the connection
		} catch (IOException e) {
			System.err.println("MSG RDR");
			e.printStackTrace();
			//this could mean the stream is unusable
			return offset < end && isOpen;
		}
	} 

	/**
	 * Close the message reader.
	 * @throws IOException
	 */
	public void close() throws IOException{
		if(in == null){
			return;
		}
		in.close();
		cb = null;
		in = null;
	}

	/**
	 * pushes all unread bytes to the begining of the byte buffer
	 */
	private void repos(){
		System.arraycopy(cb, offset, cb, 0, end-offset);
		end = end - offset;
		offset = 0;
	}

	private void grow(int i){
		byte[] tmp = new byte[i];
		System.arraycopy(cb, offset, tmp, 0, end-offset);
		cb = tmp;//this is the new bigger buffer
		end = end - offset;
		offset = 0;
	}
}
